<?php

namespace Zeke\Net\Http\Parser;


use Zeke\Zee\Util;
use Zeke\Zee\Event\EventEmitter;
use Zeke\Contracts\Io\Stream\ReadableStreamContracts;
use Zeke\Contracts\Io\Stream\WritableStreamContracts;


/**
 * http body 编码
 * [Internal] Encodes given payload stream with "Transfer-Encoding: chunked" and emits encoded data
 *
 * This is used internally to encode outgoing requests with this encoding.
 *
 */
class ChunkedEncoder extends EventEmitter implements ReadableStreamContracts
{
    private $input;
    private $closed;

    public function __construct(ReadableStreamContracts $input)
    {
        $this->input = $input;
        $this->input->on('data', array($this, 'handleData'));
        $this->input->on('end', array($this, 'handleEnd'));
        $this->input->on('error', array($this, 'handleError'));
        $this->input->on('close', array($this, 'close'));
    }


    /**
     * 是否可读
     *
     * @return bool
     *
     * @author <liu.bin@ruaby.com> 2018-08-22 15:24
     */
    public function isReadable()
    {
        return !$this->closed && $this->input->isReadable();
    }

    /**
     * 暂停
     *
     * @author <liu.bin@ruaby.com> 2018-08-22 15:24
     */
    public function pause()
    {
        $this->input->pause();
    }


    /**
     *
     * @author <liu.bin@ruaby.com> 2018-08-22 15:24
     */
    public function resume()
    {
        $this->input->resume();
    }


    /**
     *
     *
     * @param WritableStreamContracts $dest
     * @param array $options
     * @return WritableStreamContracts
     *
     *
     * @author <liu.bin@ruaby.com> 2018-08-22 15:25
     */
    public function pipe(WritableStreamContracts $dest, array $options = array())
    {
        Util::pipe($this, $dest, $options);

        return $dest;
    }


    /**
     * 关闭
     *
     * @author <liu.bin@ruaby.com> 2018-08-22 15:25
     */
    public function close()
    {
        if ($this->closed) {
            return;
        }

        $this->closed = true;
        $this->input->close();

        $this->emit('close');
        $this->removeAllListeners();
    }


    /**
     * 构造器初始化时候 注册的数据处理函数
     *
     * @param $data
     *
     * @author <liu.bin@ruaby.com> 2018-08-22 15:25
     */
    public function handleData($data)
    {
        if ($data === '') {
            return;
        }

        $completeChunk = $this->createChunk($data);

        $this->emit('data', array($completeChunk));
    }

    /**
     * 构造器初始化时候 注册的错误处理函数
     *
     * @param \Exception $e
     *
     *
     * @author <liu.bin@ruaby.com> 2018-08-22 15:26
     */
    public function handleError(\Exception $e)
    {
        $this->emit('error', array($e));
        $this->close();
    }


    /**
     * 构造器初始化时候 注册的结束处理函数
     *
     *
     * @author <liu.bin@ruaby.com> 2018-08-22 15:26
     */
    public function handleEnd()
    {
        $this->emit('data', array("0\r\n\r\n"));

        if (!$this->closed) {
            $this->emit('end');
            $this->close();
        }
    }


    /**
     * @param string $data - string to be transformed in an valid
     *                       HTTP encoded chunk string
     * @return string
     */
    private function createChunk($data)
    {
        $byteSize = dechex(strlen($data));
        $chunkBeginning = $byteSize . "\r\n";
        return $chunkBeginning . $data . "\r\n";
    }

}
